package com.canca.web.bean.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.canca.web.test.OneBean;
import com.canca.web.util.StringUtil;

public class BeanUtil {

	private static Log log = LogFactory.getLog(BeanUtil.class);
	private static BeanUtil util = new BeanUtil();
	private BeanUtil(){}
	public static BeanUtil getInstance(){
		return util;
	}
	/**
	 * po to vo
	 * @param po
	 * @param vo
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public void poToVo(Object po, Object vo) throws SecurityException,
			NoSuchMethodException, IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {
		Class poClass = po.getClass();
		Class voClass = vo.getClass();
		Method[] poMethods = poClass.getDeclaredMethods();
		for (int i = 0; i < poMethods.length; i++) {
			// method name
			String methodName = poMethods[i].getName();
			int result = isGetSetMethod(methodName);
				if (result == 0) {	//get method.
					Class poReturnType = poMethods[i].getReturnType();
					// po return type name
					String rPoTypeName = poReturnType.getName();

					// get vo's [get method]
					Method voMethod = null;
					try {
						voMethod = voClass.getMethod(methodName, null);
					} catch (Exception ex) {
						if(log.isErrorEnabled())
							log.error("po to vo 错误:",ex);
					}
					if (voMethod == null) {
						// if is null try to get the vo's [get method+Id]
						try {
							voMethod = voClass.getMethod(methodName + "Id",
									null);
						} catch (Exception ex) {
							if(log.isErrorEnabled())
								log.error("po to vo 错误:",ex);
						}
						if (voMethod == null)
							continue;
					}

					// if po's return type eq vo's return type and vo's [get
					// method]'s name eq vo's [get method]'s name then
					if (rPoTypeName.equals(voMethod.getReturnType().getName())
							&& voMethod.getName().equals(methodName)) {
						// get vo's [set method]
						String targetMethodName = new StringBuilder().append(
								"set").append(methodName.substring(3))
								.toString();
						Method targetMethod = voClass.getMethod(
								targetMethodName, poReturnType);
						targetMethod.invoke(vo, poMethods[i].invoke(po, null));
					} else { // if not
						// get the po's [get method] value.
						Object obj = poMethods[i].invoke(po, null);
						if (obj == null)
							continue;
						// try to get the [return value]'s [getId method]
						Method m = null;
						try {
							m = obj.getClass().getMethod("getId", null);
						} catch (Exception ex) {
							if(log.isErrorEnabled())
								log.error("po to vo 错误:",ex);
						}
						;
						if (m == null)
							continue;

						// get id's value.
						Object idObj = m.invoke(obj, null);

						// get the vo's [set method]
						String targetMethodName = new StringBuilder().append(
								"set").append(methodName.substring(3)).append(
								"Id").toString();
						Method targetMethod = null;
						try {
							targetMethod = voClass.getMethod(targetMethodName,
									m.getReturnType());
						} catch (Exception ex) {
							if(log.isErrorEnabled())
								log.error("po to vo 错误:",ex);
						}
						;
						if (targetMethod == null)
							continue;
						// set the po's value to vo.
						targetMethod.invoke(vo, idObj);
					}
				}
			
		}
	}

	/**
	 * po to vo
	 * @param po
	 * @param voClass
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 */
	public Object poToVo(Object po, Class voClass)
			throws InstantiationException, IllegalAccessException,
			SecurityException, IllegalArgumentException, NoSuchMethodException,
			InvocationTargetException {
		Object voObj = voClass.newInstance();
		poToVo(po, voObj);
		return voObj;
	}

	/**
	 * vo to po
	 * @param vo
	 * @param po
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 */
	public void voToPo(Object vo, Object po)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, SecurityException, NoSuchMethodException {
		Class poClass = po.getClass();
		Class voClass = vo.getClass();
		Method[] voMethods = voClass.getDeclaredMethods();
		for (int i = 0; i < voMethods.length; i++) {
			// method name
			String methodName = voMethods[i].getName();
			int result = isGetSetMethod(methodName);
				if (result == 0) {	//get method
					Class voReturnType = voMethods[i].getReturnType();
					// vo return type name
					String rVoTypeName = voReturnType.getName();

					// get po's [get method]
					Method poMethod = null;
					try {
						poMethod = poClass.getMethod(methodName, null);
					} catch (Exception ex) {
						if(log.isErrorEnabled())
							log.error("vo to po 错误:",ex);
					}
					if (poMethod == null) {
						// if is null try to get the po's [get method+Id]
						try {
							poMethod = poClass.getMethod(methodName.substring(
									0, methodName.length() - 2), null);
						} catch (Exception ex) {
							;
						}
						if (poMethod == null)
							continue;
					}

					// if vo's return type eq po's return type and po's [get
					// method]'s name eq vo's [get method]'s name then
					if (rVoTypeName.equals(poMethod.getReturnType().getName())
							&& poMethod.getName().equals(methodName)) {
						// get po's [set method]
						String targetMethodName = new StringBuilder().append(
								"set").append(methodName.substring(3))
								.toString();
						Method targetMethod = poClass.getMethod(
								targetMethodName, voReturnType);
						targetMethod.invoke(po, voMethods[i].invoke(vo, null));
					} else { // if not
						// get the vo's [get method] value.
						Object idObj = voMethods[i].invoke(vo, null);
						if (idObj == null)
							continue;
						// get po method
						Object obj = null;
						try {
							obj = poMethod.getReturnType().newInstance();
						} catch (Exception ex) {
							if(log.isErrorEnabled())
								log.error("vo to po 错误:",ex);
						}
						if (obj == null)
							continue;
						// set the Id's value.
						Method setIdMethod = null;
						try {
							setIdMethod = obj.getClass().getMethod("setId",
									voMethods[i].getReturnType());
						} catch (Exception ex) {
							if(log.isErrorEnabled())
								log.error("vo to po 错误:",ex);
						}
						if (setIdMethod == null)
							continue;
						setIdMethod.invoke(obj, idObj);

						// get the po's [set method]
						String targetMethodName = new StringBuilder().append(
								"set").append(poMethod.getName().substring(3))
								.toString();
						Method targetMethod = null;
						try {
							targetMethod = poClass.getMethod(targetMethodName,
									poMethod.getReturnType());
						} catch (Exception ex) {
							if(log.isErrorEnabled())
								log.error("vo to po 错误:",ex);
						}
						;
						if (targetMethod == null)
							continue;
						// set the vo's value to po.
						targetMethod.invoke(po, obj);
					}
				}
			
		}
	}

	/**
	 * vo to po
	 * @param vo
	 * @param poClass
	 * @return
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws InstantiationException
	 */
	public Object voToPo(Object vo, Class poClass)
			throws IllegalArgumentException, SecurityException,
			IllegalAccessException, InvocationTargetException,
			NoSuchMethodException, InstantiationException {
		Object poObj = poClass.newInstance();
		voToPo(vo, poObj);
		return poObj;
	}

	/**
	 * Map to Bean
	 * @param map
	 * @param beanObj
	 */
	public void map2Bean(Map map,Object beanObj){
		Iterator it = map.keySet().iterator();
		while(it.hasNext()){
			try{
				Object obj = it.next();
				if(!(obj instanceof java.lang.String)){
					if(log.isInfoEnabled())
						log.info("Map's Key must java.lang.String type.");
					return;
				}
				Object valueObj = map.get(obj);
				String methodName = obj.toString();
				methodName = StringUtil.getInstance().initialUpperCase(methodName);
				Class beanClass = beanObj.getClass();
				Method getMethod = beanClass.getMethod("get" + methodName, null);
				if(getMethod == null)continue;
				Class returnType = getMethod.getReturnType();
				Method setMethod = beanClass.getMethod("set" + methodName, returnType);
				if(setMethod == null)continue;
				setMethod.invoke(beanObj,ConvertUtil.getInstance().convert(valueObj,returnType));
			}catch(Exception ex){
				if(log.isErrorEnabled())
					log.error("map2Bean error!!!",ex);
			}
		}
	}
	
	/**
	 * Map to Bean
	 * @param map
	 * @param beanClass	
	 */
	public Object map2Bean(Map map,Class beanClass){
		Object obj = null;
		try {
			obj = beanClass.newInstance();
		} catch (InstantiationException e) {
			if(log.isErrorEnabled())
				log.error(e.getMessage(),e);
		} catch (IllegalAccessException e) {
			if(log.isErrorEnabled())
				log.error(e.getMessage(),e);
		}
		if(obj != null){
			map2Bean(map,obj);
			return obj;
		}
		return null;
	}
	
	/**
	 * bean object to po object.
	 * @param beanObj	bean Object
	 * @param poObj		po Object
	 * @param ignoreNULL	ignore NULL value?
	 */
	public void bean2Po(Object beanObj,Object poObj,boolean ignoreNULL){
		Method[] beanMethods = beanObj.getClass().getDeclaredMethods();
		Class poClass = poObj.getClass();
		for(int i = 0 ; i < beanMethods.length ; i++){
			String beanMethodName = beanMethods[i].getName();
			int result = isGetSetMethod(beanMethodName);
			if(result == 0){	//get method
				Object getMethodValue = null;
				try {
					getMethodValue = beanMethods[i].invoke(beanObj, null);
				} catch (Exception ex) {
					log.error("can't get the bean object's method " + beanMethodName + " value.",ex);
					continue;
				}
				if(ignoreNULL && getMethodValue == null)continue;
				Class beanReturnType = beanMethods[i].getReturnType();
				String targetMethodName = new StringBuilder().append("set").append(beanMethodName.substring(3)).toString();
				Method poMethod = null;
				try {
					poMethod = poClass.getMethod(targetMethodName, beanReturnType);
					poMethod.invoke(poObj, getMethodValue);
				} catch(Exception ex){
					log.error("can't get the po's method:" + targetMethodName,ex);
				}
			}
		}
	}
	/**
	 * bean object to po object.
	 * @param beanObj	bean Object
	 * @param poClass		po Class
	 * @param ignoreNULL	ignore NULL value?
	 */
	public Object bean2Po(Object beanObj,Class poClass,boolean ignoreNULL){
		Object poObj = null;
		try {
			poObj = poClass.newInstance();
		} catch (Exception e) {
			log.error("po Class can't not instance!",e);
			return null;
		}
		bean2Po(beanObj, poObj, ignoreNULL);
		return poObj;
	}
	
	/**
	 * Bean 转 Map
	 * @param bean
	 * @return
	 */
	public Map<String,Object> bean2Map(Object bean){
		if(bean == null){return null;}
		Map<String,Object> map = new HashMap<String,Object>();
		bean2Map(bean,map);
		return map;
	}
	
	/**
	 * Bean 转 Map
	 * @param bean
	 * @param map
	 */
	public void bean2Map(Object bean,Map<String,Object>map){
		if(bean == null){return;}
		Class beanClass = bean.getClass();
		Method[] methods = beanClass.getDeclaredMethods();
		try{
			for(Method method : methods){
				String methodName = method.getName();
				if(isGetSetMethod(methodName) == 0 ){	//get method
					Object obj = method.invoke(bean, null);
					map.put(StringUtil.getInstance().initialLowerCase(methodName.replace("get", "")), obj);
				}
			}
		}catch(Exception ex){
				log.error("bean 转 map 失败!",ex);
		}
	}
	
	/**
	 * 是否为get/set方法
	 * @param methodName	方法名字
	 * @return	-1 非get/set方法 0 get方法 1 set方法
	 */
	private int isGetSetMethod(String methodName){
		if(methodName == null || methodName.equals(""))return -1;
		if(methodName.length() <= 3)return -1;
		String top = methodName.substring(0,3);
		if(top.equals("get"))return 0;
		if(top.equals("set"))return 1;
		return -1;
	}
	
	public static void main(String[] args){
		Map map = new HashMap();
		map.put("userName", "canca");
		map.put("password", "123456");
		map.put("age", "16");
		OneBean bean = new OneBean();
		BeanUtil.getInstance().map2Bean(map,bean);
		System.out.println(bean.getUserName());
		System.out.println(bean.getPassword());
		System.out.println(bean.getAge());
	}
}
